UserAuth <- R6::R6Class(
classname = "UserAuth",
portable = TRUE,
public = list(
header = NULL,
client = NULL,
initialize = function() {
username_ <- rstudioapi::showPrompt(title = "Username", message = "Username", default = "")
password_ <- rstudioapi::askForPassword(prompt = 'Password: ')
private$user_login(username = username_, password = password_)
}
),
private = list(
user_authenticate = function(username, password) {
endpoint <- "https://api.robinhood.com/oauth2/token/"
client <- list(api_grant_type = "password", api_client_id = "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS")
detail <- paste("?grant_type=", client$api_grant_type, "&client_id=", client$api_client_id,
"&username=", username, "&password=", password, sep = "")
auth <- jsonlite::fromJSON(
rawToChar(
httr::content(httr::POST(paste(endpoint, detail, sep = "")), type = "json")
)
)
if (is.null(auth$access_token)) {
cat("\nAuthentication Failed. Please check username and password.\n\n")
return(NULL)
}
return(list(auth = auth, client = client))
},
user_login = function(username, password) {
auth <- private$user_authenticate(username = username, password = password)
client <- auth$client
auth <- auth$auth
if(is.null(auth)) {
return(NULL)
}
self$header <- c(Authorization = paste(auth$token_type, auth$access_token))
client <- c(client, tokens = list(access_token = auth$access_token, refresh_token = auth$refresh_token))
accounts <- suppressWarnings(RobinHood::api_accounts(client)) ## need to make our own api_accounts function
client <- c(client, url = list(positions = accounts$positions, accountID = accounts$url))
names(client) <- c("grantType", "ID", "accessToken", "refreshToken", "positions.url", "accountID.url")
self$client <- client
cat("\n Authentication Complete \n \n")
}
)
)
EquityData <- R6::R6Class(
classname = "EquityData",
portable = TRUE,
public = list(
tickerSymbols = NULL,
authHeader = NULL,
initialize = function(userAuthentication) {
self$authHeader <- userAuthentication$header
},
ohlcv_historicals = function(tickerSymbols, interval="day", bounds = "regular") {
if(length(tickerSymbols) == 1){
return(private$get_historical_series(tickerSymbols, interval, bounds))
}
toReturn <- lapply(as.list(tickerSymbols), function(x)
private$get_historical_series(x, interval, bounds)
)
names(toReturn) <- tickerSymbols
return(toReturn)
},
market_quote = function(tickerSymbols) {
return(private$get_quote(tickerSymbols))
},
equity_instrument_id = function(tickerSymbols) {
toReturn <- private$get_quote(tickerSymbols)
toReturn <- toReturn$instrument
toReturn <- unlist(strsplit(toReturn, "/"))
return(toReturn[5])
}
),
private = list(
get_quote = function(tickerSymbols) {
### get quotes
url <- httr::parse_url("https://api.robinhood.com/quotes/")
url$query <- list(symbols = paste(tickerSymbols, collapse = ","))
rawQuote <- suppressWarnings(
jsonlite::fromJSON(
RCurl::getForm(
httr::build_url(url), .opts=list(httpheader=self$authHeader)
)
)
)
rawQuote <- rawQuote[[1]]
### clean quotes
colnames(rawQuote) <- gsub("_", ".", colnames(rawQuote))
rawQuote[, 1:8] <- apply(rawQuote[, 1:8], 1, as.numeric)
rawQuote[, 9] <- as.Date(rawQuote[, 9])
rawQuote[, c(10,13,15)] <- apply(rawQuote[, c(10,13,15)], 2, as.character)
rawQuote[, 11:12] <- apply(rawQuote[, 11:12], 1, as.logical)
rawQuote[, 14] <- as.POSIXct(gsub("T", " ", rawQuote[, 14]), tz="UTC")
return(rawQuote)
},
get_historical_series = function(tickerSymbol, interval="day", bounds = "regular") {
if(interval != "day") {
span <- "week"
intraDay <- TRUE
} else {
span = "year"
intraDay = FALSE
}
### get historicals
url <- httr::parse_url("https://api.robinhood.com/")
url$path <- paste0("quotes/historicals/", tickerSymbol, "/")
url$query <- url$query <- list(interval = interval, span = span, bounds = bounds)
historicals <- suppressWarnings(
jsonlite::fromJSON(RCurl::getForm(httr::build_url(url), .opts=list(httpheader=self$authHeader)))
)
historicals <- historicals$historicals
historicals$begins_at <- suppressMessages(lubridate::ymd_hms(historicals$begins_at, tz = Sys.timezone()))
if(!intraDay) historicals$begins_at <- as.Date(historicals$begins_at)
historicals$open_price <- as.numeric(historicals$open_price)
historicals$close_price <- as.numeric(historicals$close_price)
historicals$high_price <- as.numeric(historicals$high_price)
historicals$low_price <- as.numeric(historicals$low_price)
toReturn <- apply(historicals[,c(2,4,5,3,6)], 2, as.numeric)
colnames(toReturn) <- c("open","high","low","close","volume")
toReturn <- xts::xts(toReturn, order.by=historicals[,1])
return(toReturn)
}
)
)
##### OptionsDataCollector #####
OptionsData <- R6::R6Class(
classname = "OptionsData",
portable = TRUE,
public = list(
tickerSymbol = NULL,
header = NULL,
chainID = NULL,
optionsMarketData = NULL,
expirationDates = NULL,
ticker_info = function(tickerSymbol) {
endpoint <- paste0("https://api.robinhood.com/instruments/?symbol=", tickerSymbol)
toReturn <- httr::content(httr::GET(url = endpoint))[[2]][[1]]
return(toReturn)
},
options_chain_id = function(tickerSymbol) {
toReturn <- self$ticker_info(tickerSymbol)
return(toReturn[["tradable_chain_id"]])
},
initialize = function(tickerSymbol, userAuthentication) {
self$tickerSymbol <- tickerSymbol
self$header <- userAuthentication$header
self$chainID <- self$options_chain_id(tickerSymbol = tickerSymbol)
contractSpecs <- private$get_specifications()
specsByTypeByExpiry <- lapply(contractSpecs[[1]], function(x) {
toReturn <- lapply(as.list(contractSpecs$expirationDates), function(y) x[x$expiration_date == y, ])
names(toReturn) <- contractSpecs$expirationDates
return(toReturn)
})
private$specsByTypeByExpiry <- specsByTypeByExpiry
self$expirationDates <- contractSpecs$expirationDates
private$contractSpecs <- contractSpecs[[1]]
},
market_quotes = function(type, expiry) {
private$get_market_quote(type, expiry)
},
historical_data = function(type, expiry, span="year") {
private$options_historicals_query(type, expiry, span)
}
),
private = list(
specsByTypeByExpiry = NULL,
contractSpecs = NULL,
optionsMarketQuotes= NULL,
optionsAddedHeader = c(
'Accept'='*/*',
'Accept-Encoding'='gzip, deflate',
'Accept-Langauge'='en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5',
'User-Agent'='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
),
get_specifications = function() {
optionsSpecs <- list()
nextURL <- "https://api.robinhood.com/options/instruments/"
requestCounter <- 0
while(!is.null(nextURL)) {
requestCounter <- requestCounter + 1
request <- suppressWarnings(
jsonlite::fromJSON(
RCurl::getForm(
nextURL, .params = list(
chain_id = self$chainID ,
rhs_tradability="tradable",
tradability="tradable",
state="active"
)
)
)
)
optionsSpecs[[requestCounter]] <- request$results
nextURL <- request$`next`
}
optionsSpecs <- as.data.frame(do.call(rbind, lapply(optionsSpecs, as.matrix)))
colnames(optionsSpecs)[which(colnames(optionsSpecs)=="url")] <- "instrument"
expirationDates <- levels(optionsSpecs$expiration_date)
optionsSpecs <- list(
call = optionsSpecs[optionsSpecs$type == "call", ],
put = optionsSpecs[optionsSpecs$type == "put", ]
)
return(list(specifications = optionsSpecs, expirationDates = expirationDates))
},
get_market_quote = function(type, expiry) {
optionsMarketDataEndpoint <- "https://api.robinhood.com/marketdata/options/"
chainQuote <- suppressWarnings(jsonlite::fromJSON(RCurl::getForm(
optionsMarketDataEndpoint, .opts = list(httpheader = c(private$optionsAddedHeader, self$header)),
.params = list(instruments = paste0(as.character(private$specsByTypeByExpiry[[type]][[expiry]]$instrument), collapse=","))
)))
chainQuote <- suppressWarnings(dplyr::inner_join(x = chainQuote$results, y = private$specsByTypeByExpiry[[type]][[expiry]], by="instrument"))
return(chainQuote)
},
options_historicals_query = function(type, expiry, span) {
possibleIntervals<-list(day="5minute",week="10minute",year="day","5year"="week")
optionsHistoricalsEndpoint = "https://api.robinhood.com/marketdata/options/historicals/"
results <- suppressWarnings(
jsonlite::fromJSON(
RCurl::getForm(
optionsHistoricalsEndpoint, .opts = list(httpheader = c(private$optionsAddedHeader, self$header)),
.params = list(
span = span,
interval = possibleIntervals[[span]],
instruments = paste0(as.character(private$specsByTypeByExpiry[[type]][[expiry]]$instrument), collapse=",")
)
)
)
)
results <- results[[1]]
historicalInstrumentIDs <- results$instrument
results <- results[[1]]
names(results) <- as.numeric(as.character(private$specsByTypeByExpiry[[type]][[expiry]]$strike_price))
return(results)
}
)
)
##### Account #####
Account <- R6::R6Class(
classname = "Account",
inherit = UserAuthentication,
portable = TRUE,
public = list(
accountMarginBalances = NULL,
accountSummary = NULL,
equityPositions = NULL,
optionsPositions = NULL,
portfolioOverview = NULL,
dayStats=NULL,
initialize = function(username, password){
super$initialize(username=username, password=password)
},
collect_account_data = function() {
private$get_account_summary_table()
self$equityPositions <- private$positions_table()
self$portfolioOverview <- private$portfolio_overview_table()
self$optionsPositions <- private$options_positions_table()
private$get_day_stats()
}
),
private=list(
portfolioEndpoint = NULL,
positionsEndpoint = NULL,
specificAccountEndpoint = NULL,
get_account_summary_table = function() {
accountEndpoint <- "https://api.robinhood.com/accounts/"
request <- suppressWarnings(
jsonlite::fromJSON(RCurl::getForm(accountEndpoint, .opts=list(httpheader=self$header)))
)
toReturn <- request$results
self$accountMarginBalances <- toReturn$margin_balances
private$portfolioEndpoint <- toReturn$portfolio
private$positionsEndpoint <- toReturn$positions
private$specificAccountEndpoint <- toReturn$url
irrelevantColumns <- c("margin_balances", "portfolio", "positions", "url", "instant_eligibility", "option_level",
"deactivated", "created_at", "is_pinnacle_account", "can_downgrade_to_cash", "user",
"withdrawal_halted", "state", "type", "sweep_enabled", "deposit_halted", "account_number",
"rhs_account_number", "active_subscription_id", "only_position_closing_trades",
"sma_held_for_orders", "max_ach_early_access_amount", "sma", "cash_balances")
relevantColumns <- c("buying_power", "cash", "cash_available_for_withdrawal", "cash_held_for_orders",
"unsettled_funds", "unsettled_debit", "uncleared_deposits", "updated_at")
toReturn <- toReturn[, which(colnames(toReturn) %in% relevantColumns)]
toReturn <- toReturn[, c(3, 5, 2, 4, 8, 6, 7, 1)]
self$accountSummary <- toReturn
},
positions_table = function() {
request <- suppressWarnings(
jsonlite::fromJSON(RCurl::getURI(private$positionsEndpoint, .opts=list(httpheader=self$header)))
)
irrelevantColumns <- c("shares_held_for_stock_grants", "account", "shares_held_for_options_events",
"created_at", "shares_pending_from_options_events", "url")
toReturn <- request$results[, -which(colnames(request$results) %in% irrelevantColumns)]
instrumentInfo <- list()
for(i in 1:nrow(toReturn)) {
tempInfo <- suppressWarnings(jsonlite::fromJSON(RCurl::getForm(toReturn$instrument[i])))
instrumentInfo[[i]] <- lapply(list("symbol", "simple_name", "name", "type"), function(x) tempInfo[[x]])
}
instrumentInfo <- do.call(rbind, lapply(instrumentInfo, c))
colnames(instrumentInfo) <- c("ticker", "short_name", "full_name", "type")
instrumentInfo <- as.data.frame(instrumentInfo, stringsAsFactors = FALSE)
toReturn <- cbind(instrumentInfo, toReturn)
positionsOrderQuantity <- sort(as.numeric(toReturn$quantity), index.return=TRUE, decreasing=TRUE)[[2]]
toReturn <- toReturn[positionsOrderQuantity, c(1, 10, 14, 6, 12, 5, 9, 13, 7, 4, 2, 3, 11, 8)] ### positions table
return(toReturn)
},
portfolio_overview_table = function () {
request <- suppressWarnings(
jsonlite::fromJSON(RCurl::getForm(private$portfolioEndpoint, .opts=list(httpheader=self$header)))
)
toReturn <- as.data.frame(request)
toReturn <- toReturn[, -c(1, 2, 4)]
return(toReturn)
},
options_positions_table = function() {
optionsPositionsEndpoint <- "https://api.robinhood.com/options/positions/"
request <- suppressWarnings(
jsonlite::fromJSON(RCurl::getForm(optionsPositionsEndpoint, .opts=list(httpheader=self$header)))
)
toReturn <- request$results
toReturn <- data.frame(
ticker = toReturn$chain_symbol,
average_price = toReturn$average_price,
quantity = toReturn$quantity,
type = toReturn$type,
pending_buy_quantity = toReturn$pending_buy_quantity,
pending_sell_quantity = toReturn$pending_sell_quantity,
id = toReturn$id,
updated_at = toReturn$updated_at
)
toReturn <- toReturn[sort(as.numeric(toReturn$quantity), index.return=TRUE, decreasing=TRUE)[[2]], ]
return(toReturn)
},
get_day_stats = function(){
portfolioURL <- paste("https://api.robinhood.com/accounts/", self$accountNumber, "/portfolio/", sep = "")
request <- suppressWarnings(
jsonlite::fromJSON(RCurl::getForm(private$portfolioEndpoint, .opts=list(httpheader=self$header)))
)
response <- request$results
lastEquity <- response$adjusted_equity_previous_close
equity <- response$equity
if(is.null(equity)){
equity <- response$extended_hours_equity
}
equity <- as.numeric(equity)
lastEquity <- as.numeric(lastEquity)
change <- equity-lastEquity
pctChange <- (change/lastEquity)*100
dayChange <- data.frame("Change($)"=change, "Percent Change"=pctChange,"Equity"=equity)
self$dayStats <- dayChange
}
)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.